#!/usr/bin/env bash

# 菜单标题
MenuTitle=""
# 菜单头部提示
MenuHeadTag=""

# 存储菜单的相关列表
MenusCmdList=()
MenusNameList=()
MenusFileList=()
SubMenusDirList=()

# 菜单的全局控制变量
CurrentFile=""
BootMenuDir=""
CurrentMenuDir=""
PreviousMenuDir=""
SubMenuExit=""
IsMakeMenuFileInit="no"

# 添加一个条目到菜单
menus_add()
{
    # name=$1
    # cmd=$2
    # submenu=$3
    if [[ -z  "$1" || -z "$2" ]]; then
        debug_echo "Menu name and cmd can't be empty!"
        return 1
    fi
    if [[ "$IsMakeMenuFileInit" == "yes" ]]; then
        if [[ "$3" != "" && "$3" != "@" ]]; then
            local now_path="${PWD:-$(pwd)}"
            [[ -d $3 ]] || mkdir $3
            cd $3 && make_init_file "$3" && $3
            cd $now_path
        fi
        return 0
    fi
    IFS=$'\n' MenusNameList+=("$1")
    IFS=$'\n' MenusCmdList+=("$2")
    if [[ "$3" == "" || "$3" == "@" ]]; then
        IFS=$'\n' SubMenusDirList+=("$(rstrip "$CurrentFile" ".sh")")
    elif [[ -d "$3" ]]; then
        # 匹配绝对路径
        IFS=$'\n' SubMenusDirList+=("$3")
    elif [[ -d "$CurrentMenuDir/$(strip "$3" "./")" ]]; then
        # 匹配相对当前目录的路径
        IFS=$'\n' SubMenusDirList+=("$CurrentMenuDir/$(strip "$3" "./")")
    elif [[ -d "$BootMenuDir/$(strip "$3" "./")" ]]; then
        # 匹配想对于菜单根目录的路径
        IFS=$'\n' SubMenusDirList+=("$BootMenuDir/$(strip "$3" "./")")
    else
        debug_echo "No found SubMenu Path:$3"
    fi
    IFS=$'\n' MenusFileList+=("$CurrentFile")
    
}

# 简单的菜单打印
menus_simple_print()
{
    wrap_string "================================================================="
    if [[ ! -z $MenuTitle ]]; then
        wrap_string "                  $MenuTitle"
        wrap_string "                                                         "
    fi
    if [[ ! -z $MenuHeadTag ]]; then
        wrap_risk_string "                 $MenuHeadTag"
        wrap_string "                                                         "
    fi
    index=0
    for menu_name in ${MenusNameList[@]}; do
        (( index < 10)) && wrap_string "                 ${index}. ${menu_name}"
        (( index >= 10)) && wrap_string "                ${index}. ${menu_name}"
        index=$[$index+1]
    done
    if [[ -z $PreviousMenuDir ]]; then
        wrap_string "                 Q or q Quit, H or h help                                   "
    else
        wrap_string "                 r or R Return, H or h help                                 "
    fi
    wrap_string "                                                               "
    wrap_string "                 Enter your selection:                         "
    wrap_string "================================================================="
}
# 打印消息，一个消息只展示一次
menus_head_message_print()
{
    [[ -z "$MENUS_HEAD_MESSAGE" ]] || echo -e "$MENUS_HEAD_MESSAGE"
    MENUS_HEAD_MESSAGE=""
    return 0
}
# 不要修改menus_print，修改menus_simple_print函数，修改菜单打印输出
menus_print()
{
    IFS=$'\n' menus_head_message_print >$MENUS_REPRINT_FILE
    IFS=$'\n' menus_simple_print >>$MENUS_REPRINT_FILE
    [ "$MENU_PUSHTOP" == "true" ] && [ "$DEBUG_LEVEL" == "nothing" ] && pushtop 
    read_file $MENUS_REPRINT_FILE
    ShowMenuFlag=
}
# 重新打印菜单
menus_reprint()
{
    local file_date="$(read_file $MENUS_REPRINT_FILE)"
    [[ -z "$file_date" || "$(read_cmd_status)" == "$CMD_STATUS_RUNNING" ]] && return 0
    echo ""
    debug_echo "Terminal size has changed, this menu will be reprinted."
    [ "$MENU_PUSHTOP" == "true" ] && [ "$DEBUG_LEVEL" == "nothing" ] && pushtop
    echo "$file_date"
    echo -n "$(read_file $PS_G_FILE)"
}
# 检查屏幕是不是被清空了
menus_check_clear()
{
    [[ -f $MENUS_REPRINT_FILE ]] || return 1
    # 获取光标位置，如果光标上方行无法显示菜单，则说明用户手动clear
    IFS=$' ' size=($(get_term_size))
    IFS=$' ' pos=($(get_cursor_pos))
    menu_lines=$(read_file_lines $MENUS_REPRINT_FILE)
    (( ${size[0]} > $menu_lines && ${pos[1]} <= $(read_file_lines $MENUS_REPRINT_FILE) )) && return 0
    return 1
}

# 检查光标是否已经到达屏幕底部
menus_check_bottom()
{
    [[ -f $MENUS_REPRINT_FILE ]] || return 1
    # 获取光标位置，如果光标在最后一行，说明已经达到了底部
    IFS=$' ' size=($(get_term_size))
    IFS=$' ' pos=($(get_cursor_pos))
    menu_lines=$(read_file_lines $MENUS_REPRINT_FILE)
    (( ${size[0]} > $menu_lines && ${pos[1]} == ${size[0]} )) && return 0
    return 1
}

# 加载菜单
menus_load()
{
    # 限制在menus_show调用或自己调用自己
    if [[ ${FUNCNAME[1]} != "menus_show" && "${FUNCNAME[1]}" != "${FUNCNAME[0]}" ]]; then
        debug_echo "This function can only be called inside the module!"
        return 1
    fi
    CurrentMenuDir=$1
    ShowMenuFlag=1
    while true; do
        MenuTitle=""
        MenuHeadTag=""
        MenusCmdList=()
        MenusNameList=()
        MenusFileList=()
        SubMenusDirList=()
        for MenuFile in $CurrentMenuDir/*.sh; do
            if [[ -f $MenuFile ]]; then
                unset menu_init
                unset menu_backtrack
                source $MenuFile
                CurrentFile=$MenuFile
                menu_init
                [[ "$SubMenuExit" != "" && "$SubMenuExit" == "${SubMenusDirList[${#SubMenusDirList[@]}-1]}" ]] && menu_backtrack && SubMenuExit=""
            fi
        done

        # 如果用户clear，重新显示菜单        
        menus_check_clear && ShowMenuFlag=1 && debug_echo "When you clear the terminal, this menu will be reprinted."

        # 打印菜单
        [[ $ShowMenuFlag ]] && menus_print

        # 读取用户输入，并应用cmd_tabmark，$(read)会创建一个**子进程**读取用户输入， 这个时候read子进程和主进程都在阻塞状态
        user_input="$(cmd_read --tab $menus_cmd_tabmark_)"
        
        #如果抵达了终端底部，下次重新显示菜单
        [ "$MENUS_CHECK" == "true" ] && menus_check_bottom && ShowMenuFlag=1 && debug_echo "When you reach the bottom of the terminal, this menu will be reprinted."
        if [[ -z $user_input ]]; then
            continue
        fi
        if [[ "$user_input" =~ ^[0-9]+$ && $user_input -lt ${#MenusCmdList[@]} ]]; then
            # 参数处理，空格拆分字符串
            IFS=$' '  cmd_and_params=(${MenusCmdList[$user_input]})
            # 执行，失败不打印菜单
            if DO_CMD ${cmd_and_params[*]}; then
                # 如果子菜单(默认与菜单脚本同名的文件夹)存在,则有下一级菜单,进入该文件夹并加载.
                SubMenu=${SubMenusDirList[$user_input]}
                if [[ -d $SubMenu ]]; then
                    PreviousMenuDir=$CurrentMenuDir
                    CurrentMenuDir=$SubMenu
                    menus_load $CurrentMenuDir
                    ShowMenuFlag=1
                else
                    file=${MenusFileList[$user_input]}
                    # 执行结束，执行退出函数
                    if [[ -f $file ]]; then
                        unset menu_backtrack
                        source $file
                        menu_backtrack
                    fi
                    ShowMenuFlag=
                fi
            fi
        elif [[ "$user_input" =~ ^[rR]$ && $PreviousMenuDir != "" && -d $PreviousMenuDir ]]; then
            # 返回上级菜单后，执行退出函数
            SubMenuExit="${CurrentMenuDir}"
            # 恢复上级环境变量，退出本级加载
            CurrentMenuDir=$PreviousMenuDir
            if [[ $PreviousMenuDir == $BootMenuDir ]]; then
                PreviousMenuDir=""
            else
                PreviousMenuDir="$(dirname $PreviousMenuDir)"
            fi
            return 0
        elif [[ "$user_input" =~ ^[qQ]$ && $PreviousMenuDir == "" ]]; then
            # 退出根目录加载，即退出整个菜单
            return 0
        elif [[ "$user_input" =~ ^[hH]$ ]]; then
            # 帮助
            DO_CMD "help"
            ShowMenuFlag=
        elif [[ "$user_input" =~ ^[cC]$ ]]; then
            # 支持 c or C clear
            DO_CMD "clear"
            ShowMenuFlag=
        else
            # 参数处理，空格拆分字符串
            IFS=$' '  user_input=($user_input)
            # 直接查找执行，失败不打印菜单
            DO_CMD ${user_input[*]}
            ShowMenuFlag=
        fi
    done
    return
}

# 检测菜单所在终端窗口的大小变化
menus_check()
{
    # 如果窗口大小发生改变，会重新打印菜单
    trap 'menus_reprint' SIGWINCH
    while [ -d $MENU_TMP_DIR ]; do
        system_sleep 0.5
    done
}

# 显示菜单
menus_show()
{
    # 这个函数会产生两个子进程，一个是menus_check &后台进程，另一个是$(cmd_read --tab $menus_cmd_tabmark_)产生的子进程，下面会用**标注。
    # 创建一个**子进程**在后台检测，该进程会随着menus模块退出而退出。
    [ "$MENUS_CHECK" == "true" ] && menus_check &
    # 获取所有命令的名字并放到数组cmd_names中
    IFS=$'\n' read -d "" -ra cmd_names <<< "$(command_name_travel)"
    # 将所有cmd_names作为Tab补全的候选词生成cmd_tabmark
    menus_cmd_tabmark_="$(create_cmd_tab ${cmd_names[*]})"
    if [[ -d $1 ]]; then
        BootMenuDir=$1
        # 菜单读取命令时，read会创建一个**子进程**读取用户输入， 这个时候read子进程和主进程都在阻塞状态
        menus_load $BootMenuDir
    fi
    #删除cmd_tabmark
    delete_cmd_tab "$menus_cmd_tabmark_"
}


make_init_file()
{
    echo "" > menu_init.sh
    echo "#!/usr/bin/env bash" >> menu_init.sh
    echo "menu_init()" >> menu_init.sh
    echo "{" >> menu_init.sh
    echo "    $1" >> menu_init.sh
    echo "    return" >> menu_init.sh
    echo "}" >> menu_init.sh
    echo "menu_backtrack()" >> menu_init.sh
    echo "{" >> menu_init.sh
    if check_system_install $1"_backtrack"; then
        echo "    "$1"_backtrack" >> menu_init.sh
    fi
    echo "    return" >> menu_init.sh
    echo "}" >> menu_init.sh
}

MAIN_MENU_ENTRY()
{
    Enter_Fun="$1" 
    IsMakeMenuFileInit="yes"
    local now_path="${PWD:-$(pwd)}"
    cd $MENUS_MAIN_DIR
    make_init_file "$Enter_Fun"
    $Enter_Fun
    cd $now_path
    IsMakeMenuFileInit="no"
    menus_show $MENUS_MAIN_DIR
}

module_init()
{
    # 菜单默认刷新置顶
    export MENU_PUSHTOP="true"
    # 默认检测菜单显示状态
    export MENUS_CHECK="true"
    export MENU_TMP_DIR="${TmpDir}/module/menu"
    export MENUS_REPRINT_FILE="${MENU_TMP_DIR}/menus_reprint_file"
    export MENUS_MAIN_DIR="${MENU_TMP_DIR}/menus_main"
    [[ -d ${MENU_TMP_DIR} ]] || mkdir -p ${MENU_TMP_DIR}
    [[ -d ${MENUS_MAIN_DIR} ]] || mkdir -p ${MENUS_MAIN_DIR}
    
    debug_echo "Menus module is ok!"
    return 0
}

module_exit()
{
    rm -rf $MENU_TMP_DIR
    debug_echo "Menus module exited!"
    return 0
}